#ifndef CAT_H
#define CAT_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stdio.h>

// 命令选项结构体
typedef struct {
    int number_lines;  // 显示行号 (-n)
    int show_ends;     // 显示行结束符 (-E)
    int squeeze_blank; // 压缩空白行 (-s)
    int verbose;       // 显示不可打印字符 (-v)
} cat_options;

// 核心函数：读取并输出文件内容
// 参数：
//   filename - 文件名（NULL表示读取标准输入）
//   options  - 命令选项
// 返回值：
//   0 - 成功
//   非0 - 错误码
int cat_file(const char* filename, const cat_options* options);

// 初始化默认选项
void cat_init_options(cat_options* options);

#ifdef __cplusplus
}
#endif

#endif // CAT_H

// 实现部分（CAT_IMPLEMENTATION 激活）
#ifdef CAT_IMPLEMENTATION

#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#ifdef _WIN32
#include <io.h>
#include <fcntl.h>
#define isatty _isatty
#define STDIN_FILENO 0
#else
#include <unistd.h>
#endif

// 初始化默认选项
void cat_init_options(cat_options* options) {
    if (options) {
        memset(options, 0, sizeof(cat_options));
    }
}

// 输出不可打印字符
static void print_verbose_char(int c) {
    if (c == 0x7F) {
        printf("^?");
    } else if (c < 0x20) {
        printf("^%c", c + 0x40);
    } else if (c >= 0x80 && c < 0xA0) {
        printf("M-^%c", c - 0x80 + 0x40);
    } else {
        putchar(c);
    }
}

// 处理并输出一行内容
static void process_line(const char* line, size_t len, const cat_options* options, unsigned long* line_num) {
    // 显示行号
    if (options->number_lines) {
        printf("%6lu  ", *line_num);
        (*line_num)++;
    }
    
    // 输出行内容
    for (size_t i = 0; i < len; i++) {
        if (options->verbose) {
            print_verbose_char((unsigned char)line[i]);
        } else {
            putchar(line[i]);
        }
    }
    
    // 显示行结束符
    if (options->show_ends) {
        putchar('$');
    }
}

// 核心函数：读取并输出文件内容
int cat_file(const char* filename, const cat_options* options) {
    cat_options default_opts;
    if (!options) {
        cat_init_options(&default_opts);
        options = &default_opts;
    }

    FILE* file = NULL;
    int is_stdin = 0;
    
    // 打开文件（NULL表示读取标准输入）
    if (filename == NULL || strcmp(filename, "-") == 0) {
        file = stdin;
        is_stdin = 1;
        
        // 在Windows上设置标准输入为二进制模式，避免换行符转换问题
#ifdef _WIN32
        _setmode(_fileno(stdin), _O_BINARY);
#endif
    } else {
        file = fopen(filename, "rb");
        if (!file) {
            perror("无法打开文件");
            return 1;
        }
    }

    unsigned long line_num = 1;
    char buffer[4096];
    size_t bytes_read;
    int prev_blank = 0;

    while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) {
        size_t start = 0;
        
        for (size_t i = 0; i < bytes_read; i++) {
            // 遇到换行符时处理一行
            if (buffer[i] == '\n') {
                size_t line_len = i - start + 1; // 包含换行符
                int is_blank = (line_len == 1);  // 仅包含换行符的行为空白行
                
                // 处理压缩空白行选项
                if (options->squeeze_blank) {
                    if (is_blank) {
                        if (prev_blank) {
                            start = i + 1;
                            continue;
                        }
                        prev_blank = 1;
                    } else {
                        prev_blank = 0;
                    }
                } else {
                    prev_blank = 0;
                }
                
                // 处理并输出当前行
                process_line(buffer + start, line_len, options, &line_num);
                start = i + 1;
            }
        }
        
        // 处理缓冲区中剩余的未换行内容
        if (start < bytes_read) {
            process_line(buffer + start, bytes_read - start, options, &line_num);
            prev_blank = 0; // 非完整行不视为空白行
        }
    }

    // 检查文件读取错误
    if (ferror(file)) {
        perror("读取文件时出错");
        if (!is_stdin) fclose(file);
        return 1;
    }

    // 关闭文件（标准输入不需要关闭）
    if (!is_stdin) {
        fclose(file);
    }

    return 0;
}

#endif // CAT_IMPLEMENTATION

// 命令行工具（CAT_MAIN 激活）
#ifdef CAT_MAIN
#include <stdio.h>
#include <string.h>

static void print_usage(const char* prog_name) {
    fprintf(stderr, "用法: %s [选项] [文件...]\n", prog_name);
    fprintf(stderr, "显示文件内容，支持标准输入\n\n");
    fprintf(stderr, "选项:\n");
    fprintf(stderr, "  -n        显示行号\n");
    fprintf(stderr, "  -E        在每行末尾显示'$'\n");
    fprintf(stderr, "  -s        压缩连续的空白行\n");
    fprintf(stderr, "  -v        显示不可打印字符\n");
    fprintf(stderr, "  --help    显示帮助信息\n");
}

// 解析命令行选项
static void parse_options(int argc, char* argv[], cat_options* options, 
                          int* file_start_idx) {
    *file_start_idx = argc; // 默认没有文件参数（读取标准输入）
    
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "--help") == 0) {
            print_usage(argv[0]);
            exit(0);
        } else if (argv[i][0] == '-') {
            // 处理选项
            for (int j = 1; argv[i][j] != '\0'; j++) {
                switch (argv[i][j]) {
                    case 'n': options->number_lines = 1; break;
                    case 'E': options->show_ends = 1; break;
                    case 's': options->squeeze_blank = 1; break;
                    case 'v': options->verbose = 1; break;
                    default:
                        fprintf(stderr, "%s: 无效选项 -- '%c'\n", argv[0], argv[i][j]);
                        fprintf(stderr, "尝试使用 '%s --help' 获取更多信息。\n", argv[0]);
                        exit(1);
                }
            }
        } else {
            // 找到第一个文件参数
            *file_start_idx = i;
            break;
        }
    }
}

int main(int argc, char* argv[]) {
    cat_options options;
    cat_init_options(&options);
    int file_start_idx;
    
    // 解析命令行选项
    parse_options(argc, argv, &options, &file_start_idx);
    
    int exit_code = 0;
    
    // 处理文件（没有文件参数则读取标准输入）
    if (file_start_idx == argc) {
        exit_code = cat_file(NULL, &options);
    } else {
        // 处理每个文件
        for (int i = file_start_idx; i < argc; i++) {
            if (cat_file(argv[i], &options) != 0) {
                exit_code = 1;
            }
        }
    }
    
    return exit_code;
}

#endif
